package roborally;

import be.kuleuven.cs.som.annotate.*;
import roborally.Exceptions.IllegalAmountException;
import roborally.Exceptions.IllegalDestinationException;

/**
 * A class of robots for playing the roborally game, involving an x - and y-position, an orientation, an energy level,
 * and a maximum amount of energy the robot can carry.
 * 
 * @invar  The X-coordinate can never be a negative number nor be greater than the greatest possible value of the type Long.
 *         | isValidXCoordinate(getX())
 * @invar  The Y-coordinate can never be a negative number nor be greater than the greatest possible value of the type Long.
 *         | isValidYCoordinate(getY())
 * @invar  The orientation has to be an integer between 0 and 3.
 *         | isValidOrientation(getOrientation())
 * @invar  The amount of energy a robot has, can never be negative nor be greater than its maximum energy.
 * 		   | isValidEnergyAmount(getEnergy(),getMaxEnergy())
 * @invar  The maxEnergy of a robot, can never be negative nor be zero. We consider these useless robots.
 * 		   | isValidMaxEnergyAmount(getMaxEnergy())
 * 
 * @version  1.34
 * @author   Michiel Ruelens & Dieter Naten
 */

public class Robot implements IRobot{

	private long x;
	private long y;
	private int orientation;
	private double energy;
	private static double maxEnergy;
	
	 /**
     * Initialize the new robot with given coordinates, orientation and energy.
     * 
     * @param  x
     * 		   The x-position of the new robot.
     * @param  y
     * 		   The y-position of the new robot.
     * @param  orientation
     * 		   The orientation of the new robot.
     * @param  energy
     *         The energy of the new robot.
     * @pre    The given x-coordinate must be valid for a robot.
     *       | isValidXCoordinate(x)
     * @pre    The given y-coordinate must be valid for a robot.
     *       | isValidYCoordinate(y)
     * @pre	   The given energy must be a valid energy for a robot.
     *       | isValidEnergy(energy)
     * @post   The x-coordinate of the new robot is equal to the given
     * 		   x-coordinate.
     *       | new.getX() == x
     * @post   The y-coordinate of the new robot is equal to the given
     * 		   y-coordinate.
     *       | new.getY() == y
     * @post   The energy of the new robot is equal to the given energy.
     *       | new.getEnergy() == energy
     */
	@Raw
	public Robot(long x, long y, int orientation, double energy) 
			throws IllegalDestinationException	{
		setX(x);
		setY(y);
		setOrientation(orientation);
		setMaxEnergy(20000);
		setEnergy(energy);	
	}

	/**
	 * This method will return the current X-coordinate of the robot.
	 */
	@Override
	@Basic
	public long getX() {
		return x;
	}
	
	/**
	 * This method will add a certain amount to the X-coordinate of the robot. 
	 * This means that the robot will move horizontally towards the right side of the board.
	 * 
	 * @param  amount
	 * 		   The amount to add to the X-coordinate of the robot.
	 * @post   The new X-coordinate of the robot is equal to the old X-coordinate plus amount. 
	 *       | (new this).getX() == this.X+amount
	 * @throws IllegalAmountException
	 * 		   The given amount is not a valid amount to add.
	 *       | ! isValidXCoordinate(this.getX()+amount)
	 */
	@Model
	private void addX(long amount) throws IllegalAmountException {
		if(!isValidXCoordinate(this.getX()+amount)){
			throw new IllegalAmountException(this,amount, "x");
		}
		this.x += amount;
		
	}
	
	/**
	 * This method will subtract a certain amount from the X-coordinate of the robot. 
	 * This means that the robot will move horizontally towards the left side of the board.
	 * 
	 * @param  amount
	 * 		   The amount to subtract from the X-coordinate of the robot.
	 * @post   The new X-coordinate of the robot is equal to the old X-coordinate minus amount. 
	 *       | (new this).getX() == this.X-amount
	 * @throws IllegalAmountException
	 * 		   The given amount is not a valid amount to subtract.
	 *       | ! isValidXCoordinate(this.getX()-amount)
	 */
	@Model
	private void subtractX(long amount) throws IllegalAmountException{
		if(!isValidXCoordinate(this.getX()-amount)){
			throw new IllegalAmountException(this,-amount, "x");
		}
		this.x -= amount;
	}
	
	/**
	 * This method sets the X-coordinate of the robot to the given X-position. 
	 * This means that the robot will move horizontally towards the right or the left side of the board.
	 * 
	 * @param  posX
	 * 		   The X-coordinate to which the robot should be moved.
	 * @post   The new X-coordinate of the robot is equal to posX. 
	 *       | (new this).getX() == posX	
	 * @throws IllegalDestinationException
	 * 		   The given coordinate posX is not a valid coordinate.
	 *       | ! isValidXCoordinate(posX)	
	 */
	@Raw
	private void setX(long posX) throws IllegalDestinationException{
		if(!isValidXCoordinate(posX)){
			throw new IllegalDestinationException(this,posX,"x");
		}
		this.x = posX;
	}
	
	/**
	 * This method checks whether the current X-coordinate of the robot is a valid one. 
	 * A valid X-coordinate is not negative, and smaller than the maximum value of the type Long.
	 * 
	 * @param  X
	 * 		   The X-coordinate that needs to be validated.
	 * @return True if X is not negative and smaller than Long.MAX_VALUE.
	 * 		 | result == (X>=0) && (X<Long.MAX_VALUE)
	 */
	public static boolean isValidXCoordinate(long X){
		return X>=0 && X<=15;

	}

	/**
	 * This method will return the current Y-coordinate of the robot.
	 */
	@Override
	@Basic
	public long getY() {
		return y;
	}
	
	/**
	 * This method will add a certain amount to the Y-coordinate of the robot. 
	 * This means that the robot will move vertically towards the bottom of the board.
	 * 
	 * @param  amount
	 * 		   The amount to add to the Y-coordinate of the robot.
	 * @post   The new Y-coordinate of the robot is equal to the old Y-coordinate plus amount. 
	 *       | (new this).getY() == this.Y+amount
	 * @throws IllegalAmountException
	 * 		   The given amount is not a valid amount to add.
	 *       | ! isValidYCoordinate(this.getY()+amount)
	 */
	@Model
	private void addY(long amount) throws IllegalAmountException{
		if(!isValidYCoordinate(this.getY()+amount)){
			throw new IllegalAmountException(this,amount, "y");
		}
		this.y += amount;
	}
	
	/**
	 * This method will subtract a certain amount from the Y-coordinate of the robot. 
	 * This means that the robot will move vertically towards the top side of the board.
	 * 
	 * @param  amount
	 * 		   The amount to subtract from the Y-coordinate of the robot.
	 * @post   The new Y-coordinate of the robot is equal to the old Y-coordinate minus amount.
	 *       | (new this).getY() == posY-amount
	 * @throws IllegalAmountException
	 * 		   The given amount is not a valid amount to subtract.
	 *       | ! isValidYCoordinate(this.getY()-amount)
	 */
	@Model
	private void subtractY(long amount) throws IllegalAmountException{
		if(!isValidYCoordinate(this.getY()-amount)){
			throw new IllegalAmountException(this,-amount, "y");
		}
		this.y -= amount;
	}
	
	/**
	 * This method sets the Y-coordinate of the robot to the given Y-position. 
	 * This means that the robot will move vertically towards the bottom or the top side of the board.
	 * 
	 * @param  posY
	 * 		   The Y-coordinate to which the robot should be moved.
	 * @post   The new Y-coordinate of the robot is equal to posY. 
	 *       | (new this).getY() == posY
	 * @throws IllegalDestinationException
	 * 		   The given coordinate posY is not a valid coordinate.
	 *       | ! isValidYCoordinate(posY)	
	 */
	@Raw
	private void setY(long posY) throws IllegalDestinationException{
		if(!isValidYCoordinate(posY)){
			throw new IllegalDestinationException(this,posY,"y");
		}
		this.y = posY;
	}
	
	/**
	 * This method checks whether the current Y-coordinate of the robot is a valid one. 
	 * A valid Y-coordinate is not negative, and smaller than the maximum value of the type Long.
	 * 
	 * @param 	Y
	 * 			The Y-coordinate that needs to be validated.
	 * @return  True if Y is not negative and smaller than Long.MAX_VALUE.
	 * 		 | result == (Y>=0) && (Y<Long.MAX_VALUE)
	 */
	public static boolean isValidYCoordinate(long Y){
		return Y>=0 && Y<=15;
	}

	/**
	 * This method will return the current orientation of the robot.
	 */
	
	@Override
	@Basic
	public int getOrientation() {
		return orientation;
	}
	
	/**
	 * This method sets the orientation of the robot to the requested orientation. 
	 * 
	 * @param  direction
	 * 		   The orientation to which the robot should be moved.
	 * @post   The new orientation of the robot is equal to direction. 
	 *       | (new this).getOrientation() == direction
	 * @throws IllegalArgumentException
	 * 		   The given direction is not a valid orientation.
	 *       | ! isValidOrientation(direction)
	 */
	@Raw
	@Model
	private void setOrientation(int direction){
		if(!isValidOrientation(direction)) { System.out.println("That is not a valid orientation."); }
		else
			{ this.orientation = direction; }
	}

	/**
	 * This method checks whether the orientation of the robot is a valid one. 
	 * A valid orientation is an integer between 0 and 3.
	 * 
	 * @param  orientation
	 * 		   The orientation that needs to be validated.
	 * @return True if orientation is and integer between 0 and 3.
	 * 		 | result == 
	 *       | (orientation>=0) && (orientation<=3)
	 */
	public static boolean isValidOrientation(int orientation){
		return orientation>=0 && orientation<=3;
	}
	
	/**
	 * This method will move the robot 1 step in the direction it is currently facing.
	 *
	 * @throws IllegalMovementException
	 * 		   When the robot is standing on the edge of the grid and facing towards the edge, a move cannot be invoked.
	 *       | ! isValidMovement(posX,posY,direction)
	 *       
	 * @post   The energy of the robot is lowered by 500 if the robot has moved.
	 * 		 |		this.energy -=500;
	 * 
	 * @effect When the robot is facing orientation '0' the robot is moved 1 step to the top.
	 * 		 | if (xORy==0 && posORneg==0)
	 * 		 |		subtractY(1);
	 * 
	 * @effect When the robot is facing orientation '1' the robot is moved 1 step to the right.
	 * 		 | if (xORy==1 && posORneg!=0)
	 * 		 |		addX(1);
	 * 
	 * @effect When the robot is facing orientation '2' the robot is moved 1 step to the bottom.
	 * 		 | if (xORy==0 && posORneg!=0)
	 * 		 |		addY(1);
	 * 
	 * @effect When the robot is facing orientation '3' the robot is moved 1 step to the left.
	 * 		 | if (xORy==1 && posORneg==0)
	 * 		 |		subtractX(1);
	 */
	@Override
	public void move(){
			if(energy>=500){
				//0 = Y-richting (0mod2 en 2mod2), 1=X-richting (1mod2 en 3mod2)
			int xORy = orientation%2;
				//0 = negatief (0mod3 en 3mod3), else: positief (1mod3 en 2mod3)
			int posORneg = orientation%3;
			if(xORy==1){
				if(posORneg==0)	{ subtractX(1); }
				else{ addX(1); }
			}
			if(xORy==0){
				if(posORneg==0)	{ subtractY(1); }
				else{ addY(1); }
			}
			this.energy -=500;
			}
			else{ System.out.println("Your robot does not have sufficient energy to move.");}
	}
	
	/**
	 * This method will turn the robot clockwise.
	 * 
	 * @effect The robot turned clockwise.
	 *        | if(this.getOrientation()==3)
	 *		  |    {this.setOrientation(0);}	
	 *        | else
	 * 		  |    {this.setOrientation(this.getOrientation()+1);}
	 */
	@Override
	public void turnClockwise() {
		if(orientation==3){
			setOrientation(0);
			this.energy -=100;
			}
		else{
			orientation++;
			this.energy -=100;
		}
	}
	
	/**
	 * This method will turn the robot anti-clockwise.
	 * 
	 * @effect The robot turned anti-clockwise.
	 *        | if(this.getOrientation()==0)
	 *		  |    {this.setOrientation(3);}	
	 *        | else
	 * 		  |    {this.setOrientation(this.getOrientation()-1);}
	 */
	public void turnAntiClockwise() {
		if(orientation==0){
			setOrientation(3);
			this.energy -=100;
			}
		else{
			orientation--;
			this.energy -=100;
		}
	}

	/**
	 * This method will turn the robot clockwise several times, equal to amount.
	 * 
	 * @effect The robot turned clockwise several times.
	 *		  | for(int i=0; i<amount; i++){
	 *		  | turnClockwise();
	 *		  | }
	 */
	public void turnClockwise(int amount) {
		for(int i=0; i<amount; i++){
			turnClockwise();
		}
	}
	
	/**
	 * This method will turn the robot anti-clockwise several times, equal to amount.
	 * 
	 * @effect The robot turned anti-clockwise several times.
	 *		  | for(int i=0; i<amount; i++){
	 *		  | turnClockwise();
	 *		  | }
	 */
	public void turnAntiClockwise(int amount) {
		for(int i=0; i<amount; i++){
			turnAntiClockwise();
		}
	}
	
	/**
	 * This method will return the current energy of the robot.
	 */
	@Override
	@Basic
	public double getEnergy() {
		return energy;
	}
	
	/**
	 * This method sets the energy of the robot to the requested energy. 
	 * 
	 * @param  energy
	 * 		   The energy which the robot should get.
	 * @pre    The amount of energy must be valid.
     *       | isValidEnergyAmount(energy,this.getMaxEnergy())   
	 * @post   The new energy of the robot is equal to energy. 
	 *       | (new this).getEnergy() == energy
	 */
	@Raw
	private void setEnergy(double energy) {
		this.energy = energy;
	}
	
	/**
	 * This method will return the (current) maxEnergy of the robot.
	 */
	@Basic
	public double getMaxEnergy() {
		return Robot.maxEnergy;
	}
	
	/**
	 * This method sets the maxEnergy of the robot to the requested maxEnergy. 
	 * 
	 * @param  maxEnergyAmount	
	 * 		   The maxEnergy which the robot should get.
	 * @pre    The amount of maxEnergy must be valid.
     *       | isValidMaxEnergyAmount(this.getMaxEnergy()) 
	 * @post   The new maxEnergy of the robot is equal to maxEnergyAmount. 
	 *       | (new this).getMaxEnergy() == maxEnergyAmount
	 */
	
	@Raw
	private void setMaxEnergy(double maxEnergyAmount) {
		Robot.maxEnergy = maxEnergyAmount;
	}

	/**
	 * This method will return the current fraction energy of the robot.
	 * 
	 * @pre    The amount of maxEnergy must be valid.
     *       | isValidMaxEnergyAmount(this.getMaxEnergy()) 
	 * @return The current fraction energy of the robot.
	 * 		 | result == 
	 * 		 | (energy/maxEnergy)
	 * 			
	 */
	@Override
	public double getFractionEnergy() {
		return (energy/Robot.maxEnergy);
	}

	 /**
	 * Check if the maxEnergy of a robot is valid.
	 * The method controls if the maxEnergy isn't negative nor zero.
	 * 
	 * @param  maxEnergy
	 * 		   The maxEnergy of the robot.
	 * @return True if the given maxEnergy is not negative nor zero.
	 *       | result ==
	 *       | (maxEnergy > 0)
	 */
	 public static boolean isValidMaxEnergyAmount(double maxEnergy){
		 return (maxEnergy > 0);
	 }
	 
	 /**
     * Check if it is possible to add a certain amount of energy to the robot, given an initial energy-level
     * and a maximum possible energy-level for the robot.
     *  
     * @param  energyAmount
     * 		   The amount of energy you want to add to the robot.
     * @param  maxEnergy
     * 		   The maximum amount of energy the robot can have.
     * @param  energy
     * 		   The amount of energy the robot initially has.
     * @return True if the given energyAmount is not negative 
     *         and if the initial energy-level plus the energyAmount do no exceed the maximum allowed energy.
     *       | result ==
     *       | (energyAmount >= 0) && (energyAmount > maxEnergy - energy)	
     */
	
	 public static boolean isAddableEnergyAmount(double energyAmount, double maxEnergy, double energy) {
	        return (energyAmount >= 0) && (energyAmount <= (maxEnergy - energy));
	 }
	
	 /**
	 * Check if a given amount of energy for a robot is valid.
	 * The method controls if the energy isn't negative nor greater
	 * than the maxEnergy of the robot.
	 * 
	 * @param  maxEnergy
	 * 		   The maximum amount of energy the robot can have.
	 * @param  energy
	 * 		   The amount of energy the robot has.
	 * @return True if the given energy is not negative 
	 *         and if the energy is smaller than the maxEnergy.
	 *       | result ==
	 *       | (energy >= 0) && (energy <= maxEnergy)	
	 */
	 public static boolean isValidEnergyAmount(double energy, double maxEnergy){
		 return (energy >= 0) && (energy <= maxEnergy);
	 }
	 
	/**
	 * This method recharges the robot with a given amount of energy.
	 * 
	 * @param 	energyAmount
	 * 			The amount of energy you want to add to the robot.
	 * @pre  	The added amount of energy must be valid, given an initial energy-level
     * 			and a maximum possible energy-level for the robot.
     *        | isValidEnergyAmount(energyAmount,this.getMaxEnergy(),this.getEnergy())    
     * @post  	The energy of the robot is the sum of the previous
     * 			energy of the robot and the energ
     * yAmount to add.
     *        | new.getEnergy() == (energy + energyAmount)
	 */
	@Override
	public void recharge(double energyAmount) {
			energy += energyAmount;
		}

	/**
	 * This method returns whether the implementations of getEnergyRequiredToReach() and moveNextTo()
	 * take into account the fact that turning consumes energy (required to score 16+).
	 * 
	 * @return  If getEnergyRequiredToReach() and moveNextTo() take in account that 
	 * 		    turning consumes energy.
	 * 		  | result ==
	 * 		  | 1
	 * @ return If getEnergyRequiredToReach() and moveNextTo() don't take in account that 
	 * 		    turning consumes energy.
	 * 		  | result ==
	 * 		  | 0
	 */
	@Override
	public int isGetEnergyRequiredToReachAndMoveNextTo16Plus() {
		return 1;
	}

	/**
	 * This method calculates the energy required for a robot to move to a certain position.
	 * 
	 * @param 	x
	 * 			The x-coordinate where the robot should move to.
	 * @param 	y
	 * 			The y-coordinate where the robot should move to.
	 * @return  The energy required to reach a certain position.
	 * 		  | result ==
	 * 		  | getEnergyRequiredToReachFromMoving(x,y) + getEnergyRequiredToReachFromTurning(x,y)
	 */
	@Override
	public double getEnergyRequiredToReach(long x, long y) {
		double movingEnergy = getEnergyRequiredToReachFromMoving(x, y);
		double turningEnergy = getEnergyRequiredToReachFromTurning(x, y);
		return turningEnergy+movingEnergy;
	}

	/**
	 * This method calculates the energy required for a robot to move to a certain position,
	 * if the given coordinates are valid. This is calculated without taking the turning of 
	 * the robot into account.
	 * 
	 * @param 	x
	 * 			The x-coordinate where the robot should move to.
	 * @param 	y
	 * 			The y-coordinate where the robot should move to.
	 * @pre  	The values of x and y must be valid coordinates. 
     *        | isValidXCoordinate(x) 
     *        | isValidYCoordinate(y)
     * @return  The energy required for the robot to reach the requested position, with taking
     * 			only moving into account
     * 		  | result ==
     *        | requiredEnergy
	 */
	@Model
	private double getEnergyRequiredToReachFromMoving(long x, long y){
		long toGoX = Math.abs(x-this.x);
		long toGoY = Math.abs(y-this.y);
		double requiredEnergy = (toGoX+toGoY)*500;
		return requiredEnergy;
	}

	/**
	 * This method calculates the energy required for a robot to move to a certain position,
	 * if the given coordinates are valid. This is calculated without taking the moving of 
	 * the robot in to account.
	 * 
	 * @param 	x
	 * 			The x-coordinate where the robot should move to.
	 * @param 	y
	 * 			The y-coordinate where the robot should move to.
	 * @pre  	The values of x and y must be valid coordinates. 
     *        | isValidXCoordinate(x) 
     *        | isValidYCoordinate(y)
     * @return  The energy required for the robot to reach the requested position, taking
     * 			only turning into account
     * 		  | result ==
     *        | requiredEnergy
	 */
	@Model
	private double getEnergyRequiredToReachFromTurning(long x, long y){
		double requiredEnergy = 0;
		int orientationX = -1; 
		int orientationY = -1;
		if(x>this.x) { 	orientationX = 1; 	}
		if(x<this.x) {	orientationX = 3;	}
		if(y>this.y) {	orientationY = 2;	}
		if(y<this.y) {	orientationY = 0;	}
		if(x == this.x && y != this.y) { requiredEnergy = checkCostFromTurning(orientation,orientationY); }
		if(y == this.y && x != this.x) { requiredEnergy = checkCostFromTurning(orientation,orientationX); }
		if(orientationX != -1 && orientationY != -1){
			if(orientation == 3 || orientation == 0){
				if(Math.abs(orientationX-orientation) == 0)		{	requiredEnergy = checkCostFromTurning(orientation,orientationY);		}
				else if(Math.abs(orientationY-orientation) == 0){ 	requiredEnergy = checkCostFromTurning(orientation,orientationX);		}
				else if(Math.abs(orientationY-orientation) == 3){	requiredEnergy = checkCostFromTurning(orientation,orientationY) + 100;	}
				else if(Math.abs(orientationX-orientation) == 3){	requiredEnergy = checkCostFromTurning(orientation,orientationX) + 100;	}
			}
			else if(Math.abs(orientationX-orientation)<(Math.abs(orientationY-orientation))){ 
				requiredEnergy = checkCostFromTurning(orientation,orientationX) + 100;
			}
			else if(Math.abs(orientationX-orientation)>(Math.abs(orientationY-orientation))){
				requiredEnergy = checkCostFromTurning(orientation,orientationY) + 100;
			}
		}
		return requiredEnergy;
	}

	/**
	 * This method checks how much energy it would cost to turn from one orientation to another.
	 * 
	 * @param originalOrientation
	 * 			The original orientation or the robot.
	 * @param destinationOrientation
	 * 			The orientation the robot would want to turn to.
	 * @return The amount of energy that is required to make the nessecary turns.
	 * 			| result == 
	 * 			| requiredEnergy
	 */
	private int checkCostFromTurning(int originalOrientation, int destinationOrientation){
		int requiredEnergy = 0;
		if(Math.abs(destinationOrientation-originalOrientation)==2)
		{
			requiredEnergy = 200;
		}
		if(((Math.abs(destinationOrientation-originalOrientation)==1)) || (Math.abs(destinationOrientation-originalOrientation)==3))
		{
			requiredEnergy = 100;
		}
		return requiredEnergy;	
	}
	
	/**
	 * Enumeration of all possible situation in which 2 robots can be, relative to each other.
	 * These situations are used to make robots move to each other.
	 */
	private static enum Situation {
		TOPRIGHT, TOPLEFT, BOTTOMRIGHT, BOTTOMLEFT, 
		TOTOP, TOBOTTOM, TORIGHT, TOLEFT, ONTOPOFEACHOTHER,
		DEFAULT;
	}

	/**
	 * This is the main method that will move 2 robots one step closer to each other, if possible. This method is recursively invoked
	 * so that in the end the 2 robots will have moved as close to each other as they possibly could.
	 * The standard routine that is being followed is:
	 * - Firstly this method will check if the robots are standing next to each other.
	 * - If this is not the case, this method will decide the situation 
	 *   (the positioning of the robots relative to each other, for example robot1 is to the bottom left of robot2).
	 * - With that information, the method determineMovementSituation() or determineMovementSituationSpecialCases()
	 *   is invoked to determine which robot should move, depending on energy and situation.
	 * - If both robots have sufficient energy to make the next move, the method determinePositioningSituation()
	 *   is invoked to determine which robot is in the most favorable orientation to move.
	 * - Lastly the proper method will be invoked depending on all previous considerations and one of the robots will make a move.
	 * - When that move is made, this method moveNextTo() is invoked again to restart the cycle.
	 * - Of course when the robots are standing next to each other, the cycle ends and this method has fulfilled it's purpose.
	 * 
	 * @param	robot1
	 * 			One of the two robots that will try to get next to each other.
	 * 
	 * @param	robot2
	 * 			The other robot that will try to get next to the first one.
	 * 
	 * @post	If the robots have sufficient energy, they will be standing next to each other when this method ends.
	 * 			They accomplish this by taking the most efficient route possible.
	 * 				|	standNextToEachOther(robot1,robot2)==true
	 * 
	 * @post	If the robots did not have sufficient energy they will have moved as close to each other as possible.
	 * 			This means that when this method ends in this case, the robots will not have enough energy to make another
	 * 			beneficial move. There are many possibilities here, but a certainty is that the robots have less than 700 energy
	 * 			otherwise they could surely make another move.
	 * 				|	robot1.getEnergy()<700 && robot2.getEnergy()<700
	 * 			
	 */
	@Override
	public void moveNextTo(IRobot robot1, IRobot robot2) {
		boolean can1Move = false;
		boolean can2Move = false;

		if(robot1.getEnergy()>=500)			{	can1Move = true; }		
		if(robot2.getEnergy()>=500)			{	can2Move = true; }	
		long x1 = robot1.getX();
		long x2 = robot2.getX();
		long y1 = robot1.getY();
		long y2 = robot2.getY();
		System.out.println(x1+" "+x2+" "+y1+" "+y2+" "+can1Move+" "+can2Move);
		
		if(!standNextToEachOther(robot1,robot2))
		{
		if(can1Move||can2Move)
			{ 
		if(x2>x1 && y2<y1)	{	determineMovementSituation(Situation.TOPRIGHT,can1Move,can2Move,robot1,robot2);		 }
		
		if(x2<x1 && y2<y1)	{	determineMovementSituation(Situation.TOPLEFT,can1Move,can2Move,robot1,robot2);		 }
		
		if(x2<x1 && y2>y1)	{	determineMovementSituation(Situation.BOTTOMLEFT,can1Move,can2Move,robot1,robot2); 	 }
	
	    if(x2>x1 && y2>y1)	{	determineMovementSituation(Situation.BOTTOMRIGHT,can1Move,can2Move,robot1,robot2);	 }
	    
		if(x1==x2 && y1==y2){	determineMovementSituationSpecialCases(Situation.ONTOPOFEACHOTHER,can1Move,can2Move,robot1,robot2); }	
		
		if(x2==x1 && y2<y1)	{	determineMovementSituationSpecialCases(Situation.TOTOP,can1Move,can2Move,robot1,robot2);	 }
		
		if(y2==y1 && x2>x1)	{	determineMovementSituationSpecialCases(Situation.TORIGHT,can1Move,can2Move,robot1,robot2);}
		
		if(x2==x1 && y2>y1)	{	determineMovementSituationSpecialCases(Situation.TOBOTTOM,can1Move,can2Move,robot1,robot2);	 }
		
		if(y2==y1 && x2<x1)	{	determineMovementSituationSpecialCases(Situation.TOLEFT,can1Move,can2Move,robot1,robot2);}
			}
		}
		}
	
	/** Checks whether 2 robots are standing next to each other.
	 * 
	 * @param robot1
	 * 			One of 2 robots that is being checked.
	 * @param robot2
	 * 			The other robot that is being checked.
	 * @return Returns true if the 2 robots are indeed standing next to each other, otherwise returns false.
	 *			|	result==
	 *			|	(robot1.getX()==robot2.getX() && Math.abs(robot2.getY()-robot1.getY())==1) ||
	 *			|	(Math.abs(robot2.getX()-robot1.getX())==1 && robot1.getY()==robot2.getY())
	 * 		
	 */
	public static boolean standNextToEachOther(IRobot robot1, IRobot robot2) {
		long x1 = robot1.getX();
		long x2 = robot2.getX();
		long y1 = robot1.getY();
		long y2 = robot2.getY();
		boolean result = false;
		if(x1==x2 && Math.abs(y2-y1)==1){result = true;}
		else if(Math.abs(x2-x1)==1 && y1==y2){result = true;}
		return result;
	}
	

	/**
	 * This method acts as an intermediate between moveNextTo and the methods that actually move the robots around
	 * (moveTopRight,moveBottomLeft, etc...) This method takes the situation (as in how are the robots positioned relative to each other)
	 * and the (energetic) possibilities of the robots as its arguments, and uses these to determine which of the methods should be invoked further.
	 * @param situation
	 * 			The positioning of the robots relative to each other.
	 * @param can1Move
	 * 			A boolean that is true when the first robot is able to make the next move.
	 * @param can2Move
	 * 			A boolean that is true when the second robot is able to make the next move.
	 * @param robot1
	 * 			The first of 2 robots that are trying to move towards each other.
	 * @param robot2
	 * 			The second of 2 robots that are trying to move towards each other.
	 */
	private void determineMovementSituation(Situation situation, boolean can1Move, boolean can2Move, IRobot robot1, IRobot robot2){
		
		int favorite = 0;
		if(situation.name().equals("TOPRIGHT")){
			
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,false,0,1,3,2,3,1);
												if(favorite==1){moveAndTurnController(robot1,robot2,0,1,3,situation);}
												else if(favorite==2){moveAndTurnController(robot2,robot1,2,3,1,situation);}
												}
			else if(can1Move)	{	moveAndTurnController(robot1,robot2,0,1,3,situation); }
			else if(can2Move)   {   moveAndTurnController(robot2,robot1,2,3,1,situation); }
		}
		if(situation.name().equals("TOPLEFT")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,false,3,0,2,1,2,0);	
												if(favorite==1){moveAndTurnController(robot1,robot2,3,0,2,situation);}
												else if(favorite==2){moveAndTurnController(robot2,robot1,1,2,0,situation);}}
			else if(can1Move)	{	moveAndTurnController(robot1,robot2,3,0,2,situation); }
			else if(can2Move)   {   moveAndTurnController(robot2,robot1,1,2,0,situation); }
		}
		if(situation.name().equals("BOTTOMLEFT")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,false,2,3,1,0,1,3);
												if(favorite==1){moveAndTurnController(robot1,robot2,2,3,1,situation);}
												else if(favorite==2){moveAndTurnController(robot2,robot1,0,1,3,situation);}}
			else if(can1Move)	{	moveAndTurnController(robot1,robot2,2,3,1,situation); }
			else if(can2Move)   {   moveAndTurnController(robot2,robot1,0,1,3,situation); }
		}
		if(situation.name().equals("BOTTOMRIGHT")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,false,1,2,0,3,0,2);
												if(favorite==1){moveAndTurnController(robot1,robot2,1,2,0,situation);}
												else if(favorite==2){moveAndTurnController(robot2,robot1,3,0,2,situation);}}
			else if(can1Move)	{	moveAndTurnController(robot1,robot2,1,2,0,situation); }
			else if(can2Move)   {   moveAndTurnController(robot2,robot1,3,0,2,situation); }
		}		
	}

	/**
	 * This method is very similar to the method 'determineMovementSituation(...)'. This method however handles
	 * more special cases of situations (i.e.: The robots are standing on top of eachother,...).
	 * This differentiation is made because these special cases require special treatment,
	 * so rather than making 1 huge method, we chose to split the problem into different smaller problems.
	 * @param situation
	 * 			The positioning of the robots relative to each other.
	 * @param can1Move
	 * 			A boolean that is true when the first robot is able to make the next move.
	 * @param can2Move
	 * 			A boolean that is true when the second robot is able to make the next move.
	 * @param robot1
	 * 			The first of 2 robots that are trying to move towards each other.
	 * @param robot2
	 * 			The second of 2 robots that are trying to move towards each other.
	 */
	private void determineMovementSituationSpecialCases(Situation situation, boolean can1Move, boolean can2Move, IRobot robot1, IRobot robot2){
		
		int favorite = 0;
		if(situation.name().equals("ONTOPOFEACHOTHER")){
			if(robot1.getEnergy()>robot2.getEnergy() && can1Move)	{	robot1.move(); 	}
			else if(can2Move)										{	robot2.move();	}
		}											
		if(situation.name().equals("TOTOP")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,true,0,3,1,2,1,3); 
													if(favorite==1){moveAndTurnControllerSpecialCases(robot1,robot2,0,3,1,2,situation);} 
													else if(favorite==2){moveAndTurnControllerSpecialCases(robot2,robot1,2,1,3,0,situation);}} 
		else if(can1Move)					{	moveAndTurnControllerSpecialCases(robot1,robot2,0,3,1,2,situation); } 
		else if(can2Move)   				{   moveAndTurnControllerSpecialCases(robot2,robot1,2,1,3,0,situation); }
		}														
		if(situation.name().equals("TOBOTTOM")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,true,2,1,3,0,3,1);
													if(favorite==1){moveAndTurnControllerSpecialCases(robot1,robot2,2,1,3,0,situation);}
													else if(favorite==2){moveAndTurnControllerSpecialCases(robot2,robot1,0,3,1,2,situation);}}
			else if(can1Move)				{	moveAndTurnControllerSpecialCases(robot1,robot2,2,1,3,0,situation); }
			else if(can2Move)   			{   moveAndTurnControllerSpecialCases(robot2,robot1,0,3,1,2,situation); }
		}														
		if(situation.name().equals("TORIGHT")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,true,1,0,2,3,2,0);
													if(favorite==1){moveAndTurnControllerSpecialCases(robot1,robot2,1,0,2,3,situation);}
													else if(favorite==2){moveAndTurnControllerSpecialCases(robot2,robot1,3,2,0,1,situation);}}
			else if(can1Move)				{	moveAndTurnControllerSpecialCases(robot1,robot2,1,0,2,3,situation); }
			else if(can2Move)  				{   moveAndTurnControllerSpecialCases(robot2,robot1,3,2,0,1,situation); }
		}															
		if(situation.name().equals("TOLEFT")){
			if(can1Move && can2Move)		{	 favorite = determinePositioningSituation(robot1,robot2,true,3,2,0,1,0,2);
													if(favorite==1){moveAndTurnControllerSpecialCases(robot1,robot2,3,2,0,1,situation);}
													else if(favorite==2){moveAndTurnControllerSpecialCases(robot2,robot1,1,0,2,3,situation);}}
			else if(can1Move)				{	moveAndTurnControllerSpecialCases(robot1,robot2,3,2,0,1,situation); }
			else if(can2Move)   			{   moveAndTurnControllerSpecialCases(robot2,robot1,1,0,2,3,situation); }
		}
	}

	/**
	 * This method is a follow-up method of determineMovementSituation(...) and is invoked if and only if
	 * both robots are able to make the next move. This method will then decide which of the two robots
	 * is in the most favorable orientation to make the next move.
	 * @param robot1
	 * 			The first of 2 robots that are trying to move towards each other.
	 * @param robot2
	 * 			The second of 2 robots that are trying to move towards each other.
	 * @param special
	 * 			A boolean that is true if the robots are in one of the special situations. (see moveNextTo)
	 * @param fo1_robot1
	 * 			The most favorable orientation that robot1 could have.
	 * @param fo2_robot1
	 * 			The second most favorable orientation that robot1 could have.
	 * @param fo3_robot1
	 * 			The third most favorable orientation that robot1 could have. (Here we assume turning clockwise is more favorable than turning anti-clockwise, for no special reason)
	 * @param fo1_robot2
	 * 			The most favorable orientation that robot2 could have.
	 * @param fo2_robot2
	 * 			The second most favorable orientation that robot2 could have.
	 * @param fo3_robot2
	 * 			The third most favorable orientation that robot2 could have. (Here we assume turning clockwise is more favorable than turning anti-clockwise, for no special reason)
	 * @return Will return 1 or 2 indicating whether robot1 or robot2 is in the most favorable orientation to move next.
	 */
	private int determinePositioningSituation(IRobot robot1, IRobot robot2, boolean special, int fo1_robot1, int fo2_robot1, int fo3_robot1,
			 									int fo1_robot2, int fo2_robot2, int fo3_robot2){
		int o1 = robot1.getOrientation();
		int o2 = robot2.getOrientation();
		int result = 1;
		if(!special){
			if(o1==fo1_robot1 || o1==fo2_robot1)		{	result = 1;	}
			else if(o2==fo1_robot2 || o2==fo2_robot2)	{	result = 2; }
			else if(o1==fo3_robot1)						{	result = 1;	}
			else if(o2==fo3_robot2)  					{   result = 2; }
		}
		if(special){
			if(o1==fo1_robot1)		 {	 result = 1; }
			else if(o2==fo1_robot2)	 {	 result = 2; }
			else if(o1==fo2_robot1)	 {	 result = 1; }
			else if(o2==fo2_robot2)  {   result = 2; }
			else if(o1==fo3_robot1)  {   result = 1; }
			else if(o2==fo3_robot2)  {   result = 2; }
		}
		return result;
	}
		
	/**
	 * This is the method that will actually move the robots one step when they are in one of the standard cases. 
	 * It will take all information collected by previous methods as its arguments,
	 * (see cycle information in the documentation of 'moveNextTo() for more information).
	 * and then make the proper robot move and/or turn.
	 * At the end of this method, the method 'moveNextTo()' is invoked to restart the cycle.
	 * @param movingRobot
	 * 			The robot that was determined by the previous methods to make the next move.
	 * @param otherRobot
	 * 			The other robot that 'movingRobot' will move closer to.
	 * @param favoriteOrientation1
	 * 			The most favorable orientation of our moving Robot.
	 * @param favoriteOrientation2
	 * 			The second most favorable orientation of our moving Robot.
	 * @param favoriteOrientation3
	 * 			The third most favorable orientation of our moving Robot.
	 * @param situation
	 * 			The positioning of the two robots relative to each other.
	 */
	private void moveAndTurnController(IRobot movingRobot,IRobot otherRobot,int favoriteOrientation1,int favoriteOrientation2,int favoriteOrientation3,Situation situation){
		
		Robot robot1 = (Robot) movingRobot;
		Robot robot2 = (Robot) otherRobot;
		boolean entered = false;
		
		if(robot1.getOrientation() == favoriteOrientation1 || robot1.getOrientation() == favoriteOrientation2)
		{
			entered = true;
		}
		else if(robot1.getOrientation() == favoriteOrientation3) 
		{
			if(robot1.getEnergy() >= 600)
			{
				robot1.turnClockwise(); 
				entered = true;
			}
		}
		else if(robot1.getEnergy() >= 600) 
		{
			robot1.turnAntiClockwise(); 
			entered = true;
		}
		if(entered)
		{
			robot1.move();
			moveNextTo(robot1,robot2);
		}
		else if(robot2.getEnergy() >= 600 && !entered)
		{
			determineMovementSituation(situation, true, false, robot2, robot1);
		}
	}

	/**
	 * This is the method that will actually move the robots one step when they are in one of the special cases. 
	 * It will take all information collected by previous methods as its arguments,
	 * (see cycle information in the documentation of 'moveNextTo() for more information).
	 * and then make the proper robot move and/or turn.
	 * At the end of this method, the method 'moveNextTo()' is invoked to restart the cycle.
	 * 
	 * @param movingRobot
	 * 			The robot that was determined by the previous methods to make the next move.
	 * @param otherRobot
	 * 			The other robot that 'movingRobot' will move closer to.
	 * @param favoriteOrientation1
	 * 			The most favorable orientation of our moving Robot.
	 * @param favoriteOrientation2
	 * 			The second most favorable orientation of our moving Robot.
	 * @param favoriteOrientation3
	 * 			The third most favorable orientation of our moving Robot.
	 * @param favoriteOrientation4
	 * 			The last possible orientation our moving Robot could have.
	 * @param situation
	 * 			The positioning of the two robots relative to each other.
	 */
	private void moveAndTurnControllerSpecialCases(IRobot movingRobot,IRobot otherRobot,int favoriteOrientation1,int favoriteOrientation2,int favoriteOrientation3,int favoriteOrientation4,Situation situation){
	
	Robot robot1 = (Robot) movingRobot;
	Robot robot2 = (Robot) otherRobot;
	boolean entered = false;
	
	if(robot1.getOrientation() == favoriteOrientation1)
	{	entered = true;	}
	else if(robot1.getOrientation() == favoriteOrientation2) 
	{
		if(robot1.getEnergy() >= 600)
		{
			robot1.turnClockwise(); 
			entered = true;
		}
	}
	else if(robot1.getOrientation() == favoriteOrientation3) 
	{		
		if(robot1.getEnergy() >= 600) 
		{
		robot1.turnAntiClockwise(); 
		entered = true;
		}
	}
	else if(robot1.getOrientation() == favoriteOrientation4) 
	{	
		if(robot1.getEnergy() >= 700) 
		{	
		robot1.turnClockwise(2); 
		entered = true;
		}
	}
	if(entered)
	{
		robot1.move();
		moveNextTo(robot1,robot2);
	}
	else if(robot2.getEnergy() >= 700 && !entered)
	{
		determineMovementSituationSpecialCases(situation, true , false, robot2, robot1);
	}
	}
}	